﻿namespace JumpJump.Offline
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using JumpJump.Runtime;
    using System.IO;
    using System.Runtime.Serialization.Formatters.Binary;

    /// <summary>
    /// TODO: Update summary.
    /// </summary>
    [Serializable]
    public class ChessManual
    {
        /// <summary>
        /// int to char
        /// </summary>
        [NonSerialized]
        public static char[] IntToChar = new char[18] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H' };

        /// <summary>
        /// the standard movement under this chessboard
        /// </summary>
        public List<ManualAction> ActionList;

        /// <summary>
        /// unique ID stand for this chessboard
        /// </summary>
        public string StatueID = string.Empty;

        /// <summary>
        /// constructor for ID/Actions
        /// </summary>
        /// <param name="id">statue ID</param>
        /// <param name="actionList">action list</param>
        public ChessManual(string id, List<ManualAction> actionList)
        {
            this.StatueID = id;
            this.ActionList = actionList;
        }

        /// <summary>
        /// constructor for board/actions
        /// </summary>
        /// <param name="boardStatue">current board statues</param>
        /// <param name="actionList">action list</param>
        public ChessManual(int[,] boardStatue, List<ManualAction> actionList)
        {
            this.StatueID = GetUniqueStatueID(boardStatue);
            this.ActionList = actionList;
        }

        /// <summary>
        /// get the unique ID accroding the current board
        /// </summary>
        /// <param name="boardStatue">current board</param>
        /// <returns>string: the unique ID</returns>
        public static string GetUniqueStatueID(int[,] boardStatue)
        {
            StringBuilder sb = new StringBuilder();

            for (int i = 0; i < Chessboard.ChessboardWidth; i++)
            {
                for (int j = 0; j < Chessboard.ChessboardHeight; j++)
                {
                    if (boardStatue[i, j] == Chessboard.EmptyPosition
                        || boardStatue[i, j] == Chessboard.IllegalPosition)
                    {
                        continue;
                    }
                    else
                    {
                        sb.Append(IntToChar[i]);
                        sb.Append(IntToChar[j]);
                    }
                }
            }

            return sb.ToString();
        }

        /// <summary>
        /// caculate the similarity of two chessboard
        /// </summary>
        /// <param name="statueFirst">1st chessboard</param>
        /// <param name="statueSecond">2nd chessboard</param>
        /// <returns>the distance of two chessboard</returns>
        public static int CaculateTheSimilarity(int[,] statueFirst, int[,] statueSecond)
        {
            return 0;
        }

        /// <summary>
        /// Load chessmanual from txtfile
        /// </summary>
        /// <param name="path">file path</param>
        /// <returns>action list</returns>
        public static List<ChessManual> LoadFromTxtFile(string path)
        {
            List<ChessManual> manualList = new List<ChessManual>();
            using (StreamReader sr = new StreamReader(path))
            {
                while (!sr.EndOfStream)
                {
                    string str = sr.ReadLine();

                    if (string.IsNullOrEmpty(str))
                    {
                        continue;
                    }

                    if (str.Length == 40)
                    {
                        string statueID = str;
                        List<ManualAction> actionList = new List<ManualAction>();
                        ChessManual manual = new ChessManual(statueID, actionList);
                        int count = int.Parse(sr.ReadLine());
                        while (count != 0)
                        {
                            string actionStr = sr.ReadLine();
                            string[] parts = actionStr.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
                            int fromX = int.Parse(parts[0]);
                            int fromY = int.Parse(parts[1]);
                            int toX = int.Parse(parts[2]);
                            int toY = int.Parse(parts[3]);
                            ManualAction ma = new ManualAction(fromX, fromY, toX, toY);
                            actionList.Add(ma);
                        }
                    }
                }
            }

            return manualList;
        }

        /// <summary>
        /// serialized a manuallist
        /// </summary>
        /// <param name="path">out dat file path</param>
        /// <param name="manualList">manual list to serialized</param>
        public static void Serialized(string path, List<ChessManual> manualList)
        {
            FileStream fs = new FileStream(path, FileMode.Create);
            BinaryFormatter bf = new BinaryFormatter();
            bf.Serialize(fs, manualList);
            fs.Close();
        }

        /// <summary>
        /// load chess manual from dat file
        /// </summary>
        /// <param name="path">dat file path</param>
        /// <returns>chess manual list</returns>
        public static List<ChessManual> LoadFromDatFile(string path)
        {
            List<ChessManual> manualList;
            FileStream fs = new FileStream(path, FileMode.Open);
            BinaryFormatter bf = new BinaryFormatter();
            manualList = bf.Deserialize(fs) as List<ChessManual>;
            fs.Close();
            return manualList;
        }

        /// <summary>
        /// merge branch chess manual list to main list
        /// </summary>
        /// <param name="raw">main chess manual list</param>
        /// <param name="insertList">branch list</param>
        public static void MergeManualDat(
            Dictionary<string, List<ManualAction>> raw,
            List<ChessManual> insertList)
        {
            foreach (ChessManual cm in insertList)
            {
                if (raw.ContainsKey(cm.StatueID))
                {
                    foreach (ManualAction ma in cm.ActionList)
                    {
                        if (!raw[cm.StatueID].Contains(ma))
                        {
                            raw[cm.StatueID].Add(ma);
                        }
                    }
                }
                else
                {
                    raw.Add(cm.StatueID, cm.ActionList);
                }
            }
        }

        /// <summary>
        /// serialized one chessmanual
        /// </summary>
        /// <param name="path">out dat path</param>
        public void Serialized(string path)
        {
            FileStream fs = new FileStream(path, FileMode.Create);
            BinaryFormatter bf = new BinaryFormatter();
            bf.Serialize(fs, this);
            fs.Close();
        }

        /// <summary>
        /// to string
        /// </summary>
        /// <returns>string of this</returns>
        public override string ToString()
        {
            StringBuilder sb = new StringBuilder();
            sb.Append(this.StatueID);
            sb.Append("\n");
            sb.Append(this.ActionList.Count);
            sb.Append("\n");
            foreach (ManualAction ma in ActionList)
            {
                sb.Append(ma.ToString());
                sb.Append("\n");
            }

            return sb.ToString();
        }

        /// <summary>
        /// equals to object
        /// </summary>
        /// <param name="obj">obj to compare</param>
        /// <returns>true false</returns>
        public override bool Equals(object obj)
        {
            ChessManual toCmp;
            if (obj is ChessManual)
            {
                toCmp = obj as ChessManual;
            }
            else
            {
                return false;
            }

            if (string.Equals(this.StatueID, toCmp.StatueID))
            {
                return true;
            }

            return false;
        }

        /// <summary>
        /// get hash code
        /// </summary>
        /// <returns>hash code</returns>
        public override int GetHashCode()
        {
            return base.GetHashCode();
        }
    }

    /// <summary>
    /// a valid manual action 
    /// </summary>
    [Serializable]
    public struct ManualAction
    {
        /// <summary>
        /// from X
        /// </summary>
        public int FromX;

        /// <summary>
        /// from Y
        /// </summary>
        public int FromY;

        /// <summary>
        /// target X
        /// </summary>
        public int ToX;

        /// <summary>
        /// target Y
        /// </summary>
        public int ToY;

        /// <summary>
        /// constructor of a new manualaction
        /// </summary>
        /// <param name="x1">from x</param>
        /// <param name="y1">from y</param>
        /// <param name="x2">target x</param>
        /// <param name="y2">target y</param>
        public ManualAction(int x1, int y1, int x2, int y2)
        {
            FromX = x1;
            FromY = y1;
            ToX = x2;
            ToY = y2;
        }

        /// <summary>
        /// override the tostring
        /// </summary>
        /// <returns>string of this</returns>
        public override string ToString()
        {
            StringBuilder sb = new StringBuilder();
            sb.Append(FromX.ToString() + " ");
            sb.Append(FromY.ToString() + " ");
            sb.Append(ToX.ToString() + " ");
            sb.Append(ToY.ToString());

            return sb.ToString();
        }

        /// <summary>
        /// equals to object
        /// </summary>
        /// <param name="obj">obj to compare</param>
        /// <returns>true false</returns>
        public override bool Equals(object obj)
        {
            ManualAction toCmp;
            if (obj is ManualAction)
            {
                toCmp = (ManualAction)obj;
            }
            else
            {
                return false;
            }

            if (this.FromX == toCmp.FromX
                && this.FromY == toCmp.FromY
                && this.ToX == toCmp.ToX
                && this.ToY == toCmp.ToY)
            {
                return true;
            }
            else
            {
                return false;
            }
        }

        /// <summary>
        /// get hash code
        /// </summary>
        /// <returns>hash code</returns>
        public override int GetHashCode()
        {
            return base.GetHashCode();
        }

        /// <summary>
        /// convert action to movement
        /// </summary>
        /// <param name="uID">user ID</param>
        /// <param name="pID">piece ID</param>
        /// <returns>generated movement</returns>
        public Movement ToMoveMent(int uID, int pID)
        {
            Coordinate from = new Coordinate(FromX, FromY);
            Coordinate to = new Coordinate(ToX, ToY);
            Movement movement = new Movement(uID, pID, from, to);
            return movement;
        }
    }
}
